﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Hack.Enumerable
{
    //public static class FEnumerable
    //{
    //    public abstract class Option<T>
    //    {
    //        public abstract bool HasValue { get; }
    //        public abstract T Value { get; }

    //        public sealed class None : Option<T>
    //        {
    //            public override bool HasValue
    //            {
    //                get { return false; }
    //            }

    //            public override T Value
    //            {
    //                get { throw new InvalidOperationException(); }
    //            }

    //            public override string ToString()
    //            {
    //                return "None<" + typeof(T).Name + ">()";
    //            }
    //        }

    //        public sealed class Some : Option<T>
    //        {
    //            private T _value;

    //            public Some(T value)
    //            {
    //                _value = value;
    //            }

    //            public override bool HasValue
    //            {
    //                get { return true; }
    //            }

    //            public override T Value
    //            {
    //                get { return _value; }
    //            }

    //            public override string ToString()
    //            {
    //                return "Some<" + typeof(T).Name + ">(" + (_value == null ? "null" : _value.ToString()) + ")";
    //            }
    //        }
    //    }
    //    public static Action<Action<Option<R>>> Bind<T, R>(this Action<Action<Option<T>>> source, Func<T, Action<Action<Option<R>>>> selector)
    //    {
    //        return o => source(x =>
    //        {
    //            if (x is Option<T>.None)
    //            {
    //                o(new Option<R>.None());
    //            }
    //            else
    //            {
    //                selector(x.Value)(y =>
    //                {
    //                    if (y is Option<R>.Some)
    //                        o(y);
    //                });
    //            }
    //        });
    //    }
    //    public static Func<Func<Option<R>>> Bind<T, R>(this Func<Func<Option<T>>> source, Func<T, Func<Func<Option<R>>>> f)
    //    {
    //        return () =>
    //        {
    //            var e = source();

    //            Option<T> lastOuter = new Option<T>.None();
    //            Option<R> lastInner = new Option<R>.None();
    //            Func<Option<R>> innerE = null;

    //            return () =>
    //            {
    //                do
    //                {
    //                    while (lastInner is Option<R>.None)
    //                    {
    //                        lastOuter = e();

    //                        if (lastOuter is Option<T>.None)
    //                        {
    //                            return new Option<R>.None();
    //                        }
    //                        else
    //                        {
    //                            innerE = f(lastOuter.Value)();
    //                        }

    //                        lastInner = innerE();
    //                        if (lastInner is Option<R>.Some)
    //                        {
    //                            return lastInner;
    //                        }
    //                    }

    //                    lastInner = innerE();
    //                } while (lastInner is Option<R>.None);

    //                return lastInner;
    //            };
    //        };
    //    }
    //    public static Func<Func<Option<T>>> Empty<T>()
    //    {
    //        return () => () => new Option<T>.None();
    //    }

    //    public static Action<Action<Option<T>>> Return<T>(T value)
    //    {
    //        return o =>
    //        {
    //            o(new Option<T>.Some(value));
    //            o(new Option<T>.None());
    //        };
    //    }
    //    /// <summary>
    //    /// Anamorphism
    //    /// </summary>
    //    public static Func<Func<Option<T>>> Ana<T>(T seed, Func<T, bool> condition, Func<T, T> next)
    //    {
    //        return () =>
    //        {
    //            Option<T> value = new Option<T>.None();
    //            return () =>
    //                condition((value = new Option<T>.Some(
    //                    value is Option<T>.None
    //                    ? seed
    //                    : next(value.Value))).Value)
    //                ? (Option<T>)new Option<T>.Some(value.Value)
    //                : (Option<T>)new Option<T>.None();
    //        };
    //    }
    //    public static IEnumerable<T> AnaIE<T>(T seed, Func<T, bool> condition, Func<T, T> next)
    //    {
    //        for (T t = seed; condition(t); t = next(t))
    //            yield return t;
    //    }
    //    public static Action<Action<Option<T>>> AnaA<T>(T seed, Func<T, bool> condition, Func<T, T> next)
    //    {
    //        return o =>
    //        {
    //            for (T t = seed; condition(t); t = next(t))
    //                o(new Option<T>.Some(t));
    //            o(new Option<T>.None());
    //        };
    //    }
    //    /// <summary>
    //    /// Opposite of Anamorphism
    //    /// </summary>
    //    public static R Cata<T, R>(this Func<Func<Option<T>>> source, R seed, Func<R, T, R> f)
    //    {
    //        var e = source();

    //        Option<T>.Some value;
    //        R result = seed;
    //        while ((value = e() as Option<T>.Some) != null)
    //        {
    //            result = f(result, value.Value);
    //        }

    //        return result;
    //    }
    //    public static R Cata<T, R>(this Action<Action<Option<T>>> source, R seed, Func<R, T, R> f)
    //    {
    //        R result = seed;

    //        bool end = false;
    //        source(x =>
    //        {
    //            if (x is Option<T>.Some && !end)
    //                result = f(result, x.Value);
    //            else
    //                end = true; // or break using exception
    //        });

    //        return result;
    //    }
    //    /// <summary>
    //    /// Converter
    //    /// </summary>
    //    public static Func<Func<Option<T>>> AsFEnumerable<T>(this IEnumerable<T> source)
    //    {
    //        return () =>
    //        {
    //            var e = source.GetEnumerator();
    //            return () => e.MoveNext()
    //                ? (Option<T>)new Option<T>.Some(e.Current)
    //                : (Option<T>)new Option<T>.None();
    //        };
    //    }
    //    public static IEnumerable<T> AsEnumerable<T>(this Func<Func<Option<T>>> source)
    //    {
    //        var e = source();
    //        Option<T>.Some value;
    //        while ((value = e() as Option<T>.Some) != null)
    //        {
    //            yield return value.Value;
    //        }
    //    }





    //    public static Func<Func<Option<int>>> Range(int from, int length)
    //    {
    //        return FEnumerable.Ana<int>(from, x => x < from + length, x => x + 1);
    //    }
    //    public static int Sum(this Func<Func<Option<int>>> source)
    //    {
    //        return source.Cata(0, (sum, x) => sum + x);
    //    }
    //    public static int Sum(this Action<Action<Option<int>>> source)
    //    {
    //        return source.Cata(0, (sum, x) => sum + x);
    //    }
    //    public static double Average(this Func<Func<Option<int>>> source)
    //    {
    //        return (double)source.Sum() / source.
    //    }
    //}
}
